package com.whut.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.whut.mapper.*;
import com.whut.model.*;
import com.whut.service.IntelligentMatchingService;
import com.whut.utils.MyDuplicatedException;
import com.whut.v_model.AllSystemId;
import com.whut.v_model.MatchedPdInfo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

@Service("intelligentMatchingService")
public class IntelligentMatchingServiceImpl implements IntelligentMatchingService {

    private final static String SUCCESS = "success";
    private final static String ERROR = "error";

    @Resource
    private NewPrefixSystemMapper newprefixsystemMapper;
    
    @Resource
    private KeywordMapper keywordMapper;
    @Resource
    private KeywordmapMapper keywordmapMapper;
    @Resource
    private VirtualkeywordmapMapper virtualkeywordmapMapper;
    @Resource
    private NewProblemDescriptionMapper newproblemdescriptionMapper;

    @Override
    public Object simpleMatch(String toMatchStr, int firstId, int secondId) {
        JSONObject jsonObject = new JSONObject();
        List<Keyword> list = keywordMapper.getAll();
        List<Integer> possibleIds = new ArrayList<Integer>();
        for (Keyword k:list) {
            if(toMatchStr.contains(k.getKeyName())){
                possibleIds.add(k.getmKey());
            }
        }
        if(possibleIds.size() == 0){
            jsonObject.put("message","noMatch");
            return jsonObject;
        }
        List<Virtualkeywordmap> v_list = virtualkeywordmapMapper.getPossibleVirs(possibleIds,firstId,secondId);
        List<Integer> pdList = getMostPossibleProblemId(v_list);
        if(pdList.isEmpty()){
            jsonObject.put("message","noMatch");
        }else if(pdList.size() == 1){
            //直接匹配
            jsonObject.put("message","singleMatch");
            jsonObject.put("res",getMatchedPdinfo(pdList.get(0)));
        }else{
            //返回选择
            jsonObject.put("message","dupMatch");
            jsonObject.put("res",newproblemdescriptionMapper.getAllById(pdList));
        }
        return jsonObject;
    }

    @Override
    public Object getSimpleMatchInfo(int pdMkey) {
        return getMatchedPdinfo(pdMkey);
    }

    private Object getMatchedPdinfo(int pdId){
        MatchedPdInfo matchedPdInfo = keywordmapMapper.getAllMatchedPdInfoByPdNum(pdId).get(0);
        List<ParPrefixSystem> thirdList = newprefixsystemMapper.getNextLevel(matchedPdInfo.getNum2());
        List<ParPrefixSystem> fourthList = newprefixsystemMapper.getNextLevel(matchedPdInfo.getNum3());
        List<ParPrefixSystem> fifthList = newprefixsystemMapper.getNextLevel(matchedPdInfo.getNum4());
        List<ParPrefixSystem> sixthList = newprefixsystemMapper.getNextLevel(matchedPdInfo.getNum5());
        List<ProblemDescription> lastList= newproblemdescriptionMapper.getLastLevel(matchedPdInfo.getNum6());
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("matchedInfo",matchedPdInfo);
        jsonObject.put("thirdList",thirdList);
        jsonObject.put("fourthList",fourthList);
        jsonObject.put("fifthList",fifthList);
        jsonObject.put("sixthList",sixthList);
        jsonObject.put("lastList",lastList);
        return jsonObject;
    }


    @Override
    public Object getKeywordMapList(int curPage, int pageSize) {
        PageHelper.startPage(curPage,pageSize);
        List<MatchedPdInfo> list = keywordmapMapper.getAllMatchedPdInfo();
        PageInfo result = new PageInfo(list);
        return result;
    }


    @Override
    @Transactional
    public void addMap(String keywords, int pdMkey,boolean allowDup) throws MyDuplicatedException {
        //1.获取一二级Id
        AllSystemId allSystemId = newprefixsystemMapper.getAllSystemIdByLastId(pdMkey);
        int sysId1 = allSystemId.getFirstId();
        int  sysId2 = allSystemId.getSecondId();
        //2.先检查关键词列表与数据库的keyword表比对
        String[] keywordsArr = keywords.split("\\.\\.\\.");
        int[] keywordsIdArr = new int[keywordsArr.length];
        for(int i = 0;i<keywordsArr.length;i++){
            List<Keyword> testKeyDupList = keywordMapper.selectByKeywordName(keywordsArr[i]);
            if(testKeyDupList.isEmpty()){
                //如果没有这个 就直接插入新的 并取得新的Id
                insertIdKey(i,keywordsArr[i],keywordsIdArr);
            }else{
                //有且完全相等或者是允许重复的情况
                int keyId =  getIsContained(testKeyDupList,keywordsArr[i]);
                //keyId 如果是0 说明是包含重复，如果不是，就表示已存在
                if(keyId!=0||allowDup){
                    if(keyId!=0){
                        keywordsIdArr[i] = keyId;
                    }else{
                        insertIdKey(i,keywordsArr[i],keywordsIdArr);
                    }
                }else{
                    //包含关系 抛出异常 事务回滚
                    throw new MyDuplicatedException("not allow dup");
                }
            }
        }
        //2 end 这个时候已经获得了 关键词的id列表
        //3 得到了需要插入的关键词的Id列表，排序之后拼成id串，检查 [keywordMap]中是否有（keywordUnionId）（problemId）与手头的id串和问题Id相同的记录，如果有，拒绝重复插入相同映射。
        Arrays.sort(keywordsIdArr);
        String keywordUnionId = String.join("_",transIntArray2StringArray(keywordsIdArr));
        Keywordmap keywordmap = keywordmapMapper.selectByKeywordUnionId(keywordUnionId,sysId1,sysId2);
        if(keywordmap != null){
            //重复映射
            throw new MyDuplicatedException("dup map");
        }
        //3 end 准备插入这条映射
        Keywordmap keywordmapToInsert = new Keywordmap();
        keywordmapToInsert.setSysId1(sysId1);
        keywordmapToInsert.setSysId2(sysId2);
        keywordmapToInsert.setProblemId(pdMkey);
        keywordmapToInsert.setKeywordUnionId(keywordUnionId);
        keywordmapToInsert.setKeywordUnionName(keywords);
        //插入映射
        updateVirtualNode_add(keywordmapToInsert,keywordsIdArr);
    }

    @Override
    public Object delMap(int mKey) {
        JSONObject jsonObject = new JSONObject();
        try {
            Keywordmap keywordmap = keywordmapMapper.selectByPrimaryKey(mKey);
            int problemId = keywordmap.getProblemId();
            JSONArray virtualList = JSONArray.parseArray(keywordmap.getVirtualNodeList());
            for (int i = 0;i<virtualList.size();i++){
                Virtualkeywordmap v = virtualkeywordmapMapper.selectByPrimaryKey(virtualList.getInteger(i));
                JSONArray mapedList = JSONArray.parseArray(v.getMapedList());
                mapedList.remove((Object) problemId);
                v.setMapedList(mapedList.toJSONString());
                virtualkeywordmapMapper.updateByPrimaryKey(v);
            }
            keywordmapMapper.deleteByPrimaryKey(mKey);
            jsonObject.put("operationCode",SUCCESS);
        }catch (Exception e) {
            e.printStackTrace();
            jsonObject.put("operationCode",ERROR);
        }
        return jsonObject;
    }


    private void insertIdKey(int index,String keyName,int[] keywordsIdArr){
        //如果没有这个 就直接插入新的 并取得新的Id
        Keyword keywordToNew = new Keyword();
        keywordToNew.setKeyName(keyName);
        keywordMapper.insertSelective(keywordToNew);
        keywordsIdArr[index] = keywordToNew.getmKey();
    }

    private String[] transIntArray2StringArray(int[] intArr){
        String[] arr = new String[intArr.length];
        for(int i = 0;i<intArr.length;i++){
            arr[i] = Integer.toString(intArr[i]);
        }
        return arr;
    }

    private void updateVirtualNode_add(Keywordmap keywordmapToInsert,int[] keywordsIdArr) {
        int[] virtualMapsMkeys = new int[keywordsIdArr.length];
        for(int i = 0;i<keywordsIdArr.length;i++){
            Virtualkeywordmap v = virtualkeywordmapMapper.selectByUniqueInfo(keywordsIdArr[i],keywordmapToInsert.getSysId1(),keywordmapToInsert.getSysId2());
            JSONArray jsonArray;
            if(v!=null){
                jsonArray = JSONArray.parseArray(v.getMapedList());
                if(!jsonArray.contains(keywordmapToInsert.getProblemId())){
                    jsonArray.add(keywordmapToInsert.getProblemId());
                }
                v.setMapedList(jsonArray.toJSONString());
                virtualkeywordmapMapper.updateByPrimaryKeySelective(v);
                virtualMapsMkeys[i] = v.getmKey();
            }else{
                Virtualkeywordmap v_new = new Virtualkeywordmap();
                jsonArray = new JSONArray();
                jsonArray.add(keywordmapToInsert.getProblemId());
                v_new.setMapedList(jsonArray.toJSONString());
                v_new.setSysId1(keywordmapToInsert.getSysId1());
                v_new.setSysId2(keywordmapToInsert.getSysId2());
                v_new.setKeywordId(keywordsIdArr[i]);
                virtualkeywordmapMapper.insertSelective(v_new);
                virtualMapsMkeys[i] = v_new.getmKey();
            }
        }
        keywordmapToInsert.setVirtualNodeList(JSON.toJSONString(virtualMapsMkeys));
        keywordmapMapper.insertSelective(keywordmapToInsert);
    }

    private int getIsContained(List<Keyword> list,String keyName){
        int len = list.size();
        for (int i = 0; i < len; i++) {
            if(keyName.equals(list.get(i).getKeyName())){
                return list.get(i).getmKey();
            }
        }
        return 0;
    }

    private List<Integer> getMostPossibleProblemId(List<Virtualkeywordmap> virs){
        List<Set<Integer>> sets = new ArrayList<Set<Integer>>();
        int len = virs.size();
        for (int i = 0; i < len; i++) {
            JSONArray jsonArray = JSONArray.parseArray(virs.get(i).getMapedList());
            List<Integer> list_int = jsonArray.toJavaList(Integer.class);
            Set<Integer> set = new HashSet<Integer>(list_int);
            sets.add(set);
        }
        List<Integer> res = bfs(sets);
        return res;
    }

    private List<Integer> bfs(List<Set<Integer>> sets) {
        int len = sets.size();
        int depth = 0;
        int depth_new = 0;
        Set<Integer> res = new HashSet<Integer>();
        Queue<String> queue = new LinkedList<String>();
        String iniStr = new String(new char[len]).replace("\0", "1");
        queue.add(iniStr);
        while(depth_new<len) {
            depth = depth_new;
            String curStr = queue.poll();
            List<Integer> curIndexs = searchAllIndex("1",curStr);
            depth_new = len-curIndexs.size();
            if(!res.isEmpty()&&depth_new!=depth) {
                break;
            }
            offerElement(queue, curIndexs, curStr);
            intersect(sets, curIndexs, res);
        }
        return new ArrayList<Integer>(res);
    }

    private List<Integer> searchAllIndex(String key,String str){
        List<Integer> list = new ArrayList<Integer>();
        int a = str.indexOf(key);//*第一个出现的索引位置
        while (a != -1) {
            list.add(a);
            a = str.indexOf(key, a + 1);//*从这个索引往后开始第一个出现的位置
        }

        return list;
    }

    private void intersect(List<Set<Integer>> sets,List<Integer> curIndexs,Set<Integer> res) {
        if(curIndexs.size()==1) {
            res.addAll(sets.get(curIndexs.get(0)));
        }else {
            Set<Integer> temp = new HashSet<Integer>();
            temp.addAll(sets.get(curIndexs.get(0)));
            for (int i = 1; i < curIndexs.size(); i++) {
                temp.retainAll(sets.get(curIndexs.get(i)));
            }
            res.addAll(temp);
        }
    }

    private void offerElement(Queue<String> queue,List<Integer> curIndexs,String curStr) {
        for (Integer integer : curIndexs) {
            StringBuilder sb = new StringBuilder(curStr);
            sb.replace(integer, integer+1, "0");
            queue.offer(sb.toString());
        }
    }

    @Override
    public Object getVoiceMapInfoBySystemId(String system,int curPage,int pageSize) {
        PageHelper.startPage(curPage,pageSize);
        JSONArray jsonArray=JSONArray.parseArray(system);
        int length=jsonArray.size();
        int number=jsonArray.getIntValue(length-1);
        String num="";
        switch (length)
        {
            case 1:
                num="num1";
                break;
            case 2:
                num="num2";
                break;
            case 3:
                num="num3";
                break;
            case 4:
                num="num4";
                break;
            case 5:
                num="num5";
                break;
            case 6:
                num="num6";
                break;
                default:

        }
        List<MatchedPdInfo> list=keywordmapMapper.getMatchedPdInfoByNumberAndNum(number,num);
        PageInfo result = new PageInfo(list);
        return result;
    }
}

